﻿namespace BlogsAPI.Search
{
    using System.Diagnostics;
    using RssToolkit.Rss;
    using Interfaces;
    using System;
    using System.Web;
    using Abstract;

    /// <summary>
    /// Класс для работы с поиском по блогам от яндекса
    /// </summary>
    public class BlogsYandex : BasicSearch
    {
        /// <summary>
        /// максимальное число подгружаемых в поиске страниц
        /// </summary>
        private const int MAXPAGES = 20;

        delegate String FormSearchLink(String text, Boolean rss = false, int page = 0);

        /// <summary>
        /// Конструктор
        /// </summary>
        public BlogsYandex()
        {
            separator_and = "&&";
            separator_or = "||";
            separator_not = "-";
        }

        /// <summary>
        /// Формируем ссылку для поиска в яндексе числа отзывов и комментов
        /// </summary>
        /// <param name="text">текст для поиска</param>
        /// <param name="rss">выдача в RSS или XML</param>
        /// <param name="page">номер страницы, начинается с 0</param>
        /// <returns>Ссылка для поиска в Яндексе</returns>
        protected virtual String FormSearchByText(String text, Boolean rss = false, int page = 0)
        {
            return String.Format("http://blogs.yandex.ru/search.{1}?text={0}&p={2}",
                HttpUtility.UrlEncode(text).Replace("&", "%26"), rss ? "rss" : "xml", page);
        }

        /// <summary>
        /// Формируем ссылку для поиска в яндексе
        /// </summary>
        /// <param name="link">ссылка на ресурс</param>
        /// <param name="rss">выдача в RSS или XML</param>
        /// <param name="page">номер страницы, начинается с 0</param>
        /// <returns>Ссылка для поиска в Яндексе</returns>
        protected virtual String FormSearchByLink(String link, Boolean rss = false, int page = 0)
        {
            return String.Format("http://blogs.yandex.ru/search.{1}?link={0}&ft=blog&p={2}",
                                     HttpUtility.UrlEncode(link).Replace("&", "%26"), rss ? "rss" : "xml", page);
        }

        /// <summary>
        /// Формирование разобранного поста из того что дает яндекс.
        /// </summary>
        /// <param name="yad"></param>
        /// <returns></returns>
        private BlogPost ParseBlogPost(RssItemYandex yad)
        {
            string sLink = yad.Link.ToLower();
            //две важные ошибки яндекса надо исправить
            //1) правильную ссылку на твиттер
            if(sLink.Contains("/twitter.com/"))
            {
                sLink = sLink.Replace("/statuses/", "/status/");
            }
            //2) правильную ссылку на вконтакте
            else if(sLink.Contains("/vk.com/") && sLink.Contains("?status="))
            {
                sLink = sLink.Replace("?status=", "_").Replace("/id", "/wall");
            }
            return new BlogPost
            {
                Link = new Uri(sLink),
                Body = yad.Description,
                Subject = yad.Title,
                Existance = true,
                SpecialParam = yad.Journal,
                Published = yad.PubDateParsed
            };
        }

        /// <summary>
        /// Функция делает запрос к яндексу и возвращает резульат
        /// </summary>
        /// <param name="getLink">функция получения запросной ссылки</param>
        /// <param name="sParam">основной параметр</param>
        /// <param name="fromDate">дата ограничение</param>
        /// <returns></returns>
        private BlogPost[] ProcessRequest(FormSearchLink getLink, string sParam, DateTime? fromDate)
        {
            BlogPost[] oRes = new BlogPost[0];
            try
            {
                for (int p = 0; p < MAXPAGES; p++)
                {
                    RssDocument rss = RssDocument.Load(new Uri(getLink(sParam, true, p)), false);
                    if (rss != null)//разобрали, и начинаем считать
                    {
                        foreach (RssItemYandex t in rss.Channel.Items)
                        {
                            //если пост слишком старый, то заканчиваем работу.
                            if ((fromDate != null && t.PubDateParsed < fromDate)
                                || rss.Channel.YaCount < oRes.Length)//слишком 
                            {
                                p = MAXPAGES;
                                break;
                            }
                            BlogPost bp = ParseBlogPost(t);
                            if (bp != null)
                            {
                                Array.Resize(ref oRes, oRes.Length + 1);
                                oRes[oRes.Length - 1] = bp;
                            }
                        }
                        //если слишком мало данных, то нет смысла проходить все страницы дальше
                        if (rss.Channel.Items.Count < 10)
                        {
                            break;
                        }
                    }else
                    {
                        break;//skip other pages
                    }
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("SearchFor failed cause " + e1.Message);
            }
            return oRes;

        }

        /// <summary>
        /// Все посты с нужным текстом
        /// </summary>
        /// <param name="text">текст для поиска</param>
        /// <param name="fromDate">дата начиная с которой искать объекты, может быть нулом, тогде по максимуму!</param>
        /// <returns>массив постов удовлетворяющих поиску</returns>
        public override BlogPost[] SearchFor(String text, DateTime? fromDate)
        {
            return ProcessRequest(FormSearchByText, text, fromDate);
        }

        /// <summary>
        /// Все посты ссылающиеся на указанный ресурс
        /// </summary>
        /// <param name="link">ссылка на ресурс</param>
        /// <param name="fromDate">дата начиная с которой искать объекты, может быть нулом, тогде по максимуму!</param>
        /// <returns>массив постов удовлетворяющих поиску</returns>
        public override BlogPost[] SearchFor(Uri link, DateTime? fromDate)
        {
            return ProcessRequest(FormSearchByLink, link.OriginalString, fromDate);
        }
    }
}
